/* RISC-V kernel boot stage */
#include <context.h>
#include <os/elf.h>
#include <pgtable.h>
#include <sbi.h>

typedef void (*kernel_entry_t)(unsigned long);

extern unsigned char _elf_main[];
extern unsigned _length_main;

/********* setup memory mapping ***********/
/*uintptr_t alloc_page()
{
    // TODO: alloc pages for page table entries
}*/

// using 2MB large page
void map_page(uint64_t vpn, uint64_t pa, PTE *pgdir,int level)
{
    // TODO: map va to pa
    pgdir[vpn]=(uint64_t)PA2PTE(pa);
    if(level == 2){
        pgdir[vpn]+=0xef;
    }else
        pgdir[vpn]+=0xe1;
}

void enable_vm()
{
    // TODO: write satp to enable paging
    // remember to flush TLB
    set_satp(SATP_MODE_SV39,1,PGDIR_PA>>NORMAL_PAGE_SHIFT);
    local_flush_tlb_all();
}

/* Sv-39 mode
 * 0x0000_0000_0000_0000-0x0000_003f_ffff_ffff is for user mode
 * 0xffff_ffc0_0000_0000-0xffff_ffff_ffff_ffff is for kernel mode
 */
void setup_vm()
{
    // TODO:
    clear_pgdir(PGDIR_PA);
    PTE *second_level_kpagetable = PGDIR_PA+NORMAL_PAGE_SIZE;
    map_page(1,PGDIR_PA+NORMAL_PAGE_SIZE, PGDIR_PA,1);//2nd level pagetable for 0x5020_0000 ~ 0x5040_0000
    map_page(0x101,PGDIR_PA+NORMAL_PAGE_SIZE,PGDIR_PA,1); // 2nd level for kernelimage
    uint64_t p;
    uint64_t base = 0x10000000;
    for(p=0;p+LARGE_PAGE_SIZE <= 0x20000000 ; p+=LARGE_PAGE_SIZE)
        map_page((base+p)>>LARGE_PAGE_SHIFT,0x50000000+p,second_level_kpagetable,2);
    // map kernel virtual address(kva) to kernel physical
    // address(kpa) kva = kpa + 0xffff_ffc0_0000_0000 use 2MB page,
    // map all physical memory

    // enable virtual memory
    enable_vm();
}

uintptr_t directmap(uintptr_t kva, uintptr_t pgdir)
{
    // ignore pgdir

    return kva;
}

kernel_entry_t start_kernel = NULL;

/*********** start here **************/
int boot_kernel(unsigned long mhartid)
{
    sbi_console_putchar('1');
    if (mhartid == 0) {
        setup_vm();
        // load kernel
        sbi_console_putchar('2');
        start_kernel =
            (kernel_entry_t)load_elf(_elf_main, _length_main,
                                     PGDIR_PA, directmap);
        sbi_console_putchar('3');
    } else {
        // TODO: what should we do for other cores?
    }
    start_kernel(mhartid);
    return 0;
}
